package gagak.framework.web.paramresovler.requestresponse;

import gagak.framework.web.annonation.HttpMethod;
import gagak.framework.web.annonation.Nullable;
import gagak.framework.web.param.MethodParameter;
import gagak.framework.web.paramresovler.HandlerMethodArgumentResolver;
import gagak.framework.web.support.request.NativeWebRequest;
import gagak.framework.web.support.request.WebRequest;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.security.Principal;
import java.util.Locale;
import java.util.TimeZone;

/*
 * @program: ce
 * @description: 解析handler参数上的HttpServletRequest
 * @author: houhong
 * @create: 2022-11-25 21:48
 */
public class ServletRequestMethodArgumentResolver implements HandlerMethodArgumentResolver {


    /*
     * 这种数据类型都由他来解析
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        Class<?> paramType = methodParameter.getParameterType();
        return (ServletRequest.class.isAssignableFrom(paramType) |
                HttpSession.class.isAssignableFrom(paramType) ||
                Principal.class.isAssignableFrom(paramType) ||
                InputStream.class.isAssignableFrom(paramType) ||
                Reader.class.isAssignableFrom(paramType) ||
                HttpMethod.class == paramType ||
                Locale.class == paramType ||
                TimeZone.class == paramType)
                ;
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, NativeWebRequest webRequest) throws IOException {

        Class<?> paramType = parameter.getParameterType();

        if (WebRequest.class.isAssignableFrom(paramType)) {
            if (!paramType.isInstance(webRequest)) {
                throw new IllegalStateException(
                        "Current request is not of type [" + paramType.getName() + "]: " + webRequest);
            }
            return webRequest;
        }
        return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class));
    }



    private <T> T resolveNativeRequest(NativeWebRequest webRequest, Class<T> requiredType) {
        T nativeRequest = webRequest.getNativeRequest(requiredType);
        if (nativeRequest == null) {
            throw new IllegalStateException(
                    "Current request is not of type [" + requiredType.getName() + "]: " + webRequest);
        }
        return nativeRequest;
    }

    @Nullable
    private Object resolveArgument(Class<?> paramType, HttpServletRequest request) throws IOException {
        if (HttpSession.class.isAssignableFrom(paramType)) {
            HttpSession session = request.getSession();
            if (session != null && !paramType.isInstance(session)) {
                throw new IllegalStateException(
                        "Current session is not of type [" + paramType.getName() + "]: " + session);
            }
            return session;
        } else if (InputStream.class.isAssignableFrom(paramType)) {
            InputStream inputStream = request.getInputStream();
            if (inputStream != null && !paramType.isInstance(inputStream)) {
                throw new IllegalStateException(
                        "Request input stream is not of type [" + paramType.getName() + "]: " + inputStream);
            }
            return inputStream;
        } else if (Reader.class.isAssignableFrom(paramType)) {
            Reader reader = request.getReader();
            if (reader != null && !paramType.isInstance(reader)) {
                throw new IllegalStateException(
                        "Request body reader is not of type [" + paramType.getName() + "]: " + reader);
            }
            return reader;
        } else if (Principal.class.isAssignableFrom(paramType)) {
            Principal userPrincipal = request.getUserPrincipal();
            if (userPrincipal != null && !paramType.isInstance(userPrincipal)) {
                throw new IllegalStateException(
                        "Current user principal is not of type [" + paramType.getName() + "]: " + userPrincipal);
            }
            return userPrincipal;
        } else if (HttpMethod.class == paramType) {
            return HttpMethod.resolve(request.getMethod());
        }

        throw new UnsupportedOperationException("Unknown parameter type: " + paramType.getName());
    }
}